use crate::Line2::Line2;
use crate::MathUtils::MathUtils;
use crate::Matrix3x3::Matrix3x3;
use crate::Point::Point;
use crate::Rect::Rect;
use crate::Segment::Segment;
use crate::Vector2::Vector2;

/**
 * polygon，常用的七种简单关系
 *
 *   *******       &&&&&&&
 *   *     *       &     &        相离（外）
 *   *     *       &     &
 *   *******       &&&&&&&
 *
 *    *******&&&&&&&
 *    *     *&     &        相切(外）
 *    *     *&     &
 *    *******&&&&&&&
 *
 *
 *   ******&&&&&&&&&
 *   *    & *     &        相交
 *   *    & *     &
 *   *****&&&&&&&&&
 *
 *
 *
 *   ********
 *   *  & & *       相切（内）1包含2，或者2包含1
 *   *  & & *
 *   ********
 *
 *
 *   *********
 *   *       *
 *   *  & &  *       相离（内）1包含2，或者2包含1
 *   *  & &  *
 *   *       *
 *   *********
 *
 *
 */
pub struct Polygon {
    pub vertices: Vec<Point>,
    pub edges: Vec<Segment>,
}

impl Polygon {
    pub fn new(vcs: Vec<Point>) -> Self {
        if vcs.len() < 3 {
            panic!("顶点不到三个！");
        }
        let mut vertices = Vec::new();
        for item in vcs {
            vertices.push(Point::new(item.x, item.y));
        }
        let edges = Self::update_edges(&vertices);
        Self { vertices, edges }
    }

    pub fn polygon_rotate(&self, angle_rad: f64, origin: &Point) -> Vec<Point> {
        let mut mat = Matrix3x3::new();
        mat.translate(origin.x, origin.y);
        mat.rotate(angle_rad);
        let mut vs = Vec::new();
        for item in &self.vertices {
            let (mut p_x, mut p_y) = mat.apply_inverse((item.x, item.y));
            p_x += origin.x;
            p_y += origin.y;
            vs.push(Point::new(p_x, p_y));
        }
        vs
    }

    pub fn polygon_ray_cast(
        &self,
        orig_origin: Point,
        holes: &Vec<Polygon>,
        angle_rad: f64,
        ep: Option<f64>,
    ) -> Vec<Option<Point>> {
        let alpha = angle_rad;
        let vector = Vector2::new(alpha.cos(), alpha.sin());
        let normal = vector.ccw_normal();
        let line = Line2::new(orig_origin, normal);
        let idx = if MathUtils::equal(alpha, std::f64::consts::PI / 2.0, ep) {
            1
        } else {
            0
        };
        let origin_value = if idx == 0 {
            orig_origin.x
        } else {
            orig_origin.y
        };
        let mut min_sq_dist_left = f64::MAX;
        let mut min_sq_dist_right = f64::MAX;
        let mut closest_point_left = None;
        let mut closest_point_right = None;
        let mut edges = self.edges.clone();
        for item in holes {
            edges.append(&mut item.edges.clone());
        }
        for edge in edges {
            let p = line.intersect_seg(&edge, ep);
            if let Some(p) = p {
                let sq_dist = orig_origin.distance_to_point_squared(&p);
                let p_value = if idx == 0 { p.x } else { p.y };
                if p_value < origin_value {
                    if sq_dist < min_sq_dist_left {
                        min_sq_dist_left = sq_dist;
                        closest_point_left = Some(p);
                    }
                } else if p_value > origin_value {
                    if sq_dist < min_sq_dist_right {
                        min_sq_dist_right = sq_dist;
                        closest_point_right = Some(p);
                    }
                }
            }
        }
        vec![closest_point_left, closest_point_right]
    }

    pub fn update_edges(vertices: &Vec<Point>) -> Vec<Segment> {
        let mut edges = Vec::new();
        let mut last_v = vertices[vertices.len() - 1];
        for item in vertices {
            let seg = Segment::new(last_v, item.clone());
            edges.push(seg);
            last_v = *item;
        }
        edges
    }

    pub fn Rect(&self) -> Rect {
        let xs: Vec<f64> = self.vertices.iter().map(|i| i.x).collect();
        let mut min_x = f64::MAX;
        let mut max_x = f64::MIN;
        let mut min_y = f64::MAX;
        let mut max_y = f64::MIN;
        for v in self.vertices.iter() {
            if v.x.is_nan() || v.y.is_nan() {
                continue;
            }
            if v.x < min_x {
                min_x = v.x;
            }
            if v.x > max_x {
                max_x = v.x;
            }
            if v.y < min_y {
                min_y = v.y;
            }
            if v.y > max_y {
                max_y = v.y;
            }
        }
        Rect::new(Point::new(min_x, min_y), Point::new(max_x, max_y))
    }
}
